package com.example.personalizededucationsystem.service;

import com.example.personalizededucationsystem.dto.dto.AssignmentStatDto;
import com.example.personalizededucationsystem.dto.dto.ClassAnalyticsResponseDto;
import com.example.personalizededucationsystem.dto.dto.CommonErrorQuestionDto;
import com.example.personalizededucationsystem.model.*;
import com.example.personalizededucationsystem.model.Class;
import com.example.personalizededucationsystem.repository.AnswerRecordRepository;
import com.example.personalizededucationsystem.repository.ClassMemberRepository;
import com.example.personalizededucationsystem.repository.ClassRepository;
import com.example.personalizededucationsystem.repository.StudentAssignmentRepository;
import jakarta.persistence.EntityNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class AnalysisService {


    private static final int RECENT_ANSWERS_LIMIT = 10;
    private static final int COMMON_ERRORS_LIMIT = 5;
    private final AnswerRecordRepository answerRecordRepository;
    private final ClassRepository classRepository;
    private final ClassMemberRepository classMemberRepository;
    private final StudentAssignmentRepository studentAssignmentRepository;

    @Autowired
    public AnalysisService(AnswerRecordRepository answerRecordRepository, ClassRepository classRepository, ClassMemberRepository classMemberRepository, StudentAssignmentRepository studentAssignmentRepository) {
        this.answerRecordRepository = answerRecordRepository;
        this.classRepository = classRepository;
        this.classMemberRepository = classMemberRepository;
        this.studentAssignmentRepository = studentAssignmentRepository;
    }

    /**
     * 计算学生在指定学科下各知识点的掌握度。
     * <p>
     * 掌握度基于该学生最近若干次（由常量 {@code RECENT_ANSWERS_LIMIT} 限制）答题记录的正确率计算。
     * 方法首先筛选出与指定学科相关的答题记录（即包含该学科知识点的问题），然后对每个知识点计算掌握度。
     * </p>
     *
     * @param student 当前学生用户
     * @param subject 指定的学科名（如 "数学", "物理"）
     * @return 一个 Map，键为知识点名称，值为该知识点的掌握度（0.0~1.0 之间的浮点数）
     */

    public Map<String, Double> calculateStudentMastery(User student, String subject) {
        List<AnswerRecord> allRecords = answerRecordRepository.findAllByStudentWithDetails(student).stream()
                .filter(record -> record.getQuestion().getKnowledgePoints().stream()
                        .anyMatch(kp -> subject.equals(kp.getSubject())))
                .toList();

        Map<KnowledgePoint, List<AnswerRecord>> recordsByKp = allRecords.stream()
                .flatMap(record -> record.getQuestion().getKnowledgePoints().stream()
                        .filter(kp -> subject.equals(kp.getSubject()))
                        .map(kp -> new KpRecordPair(kp, record)))
                .collect(Collectors.groupingBy(KpRecordPair::kp,
                        Collectors.mapping(KpRecordPair::record, Collectors.toList())));
        return recordsByKp.entrySet().stream()
                .collect(Collectors.toMap(entry -> entry.getKey().getName(), entry -> {
                    List<AnswerRecord> kpRecords = entry.getValue();
                    List<AnswerRecord> recentRecords = kpRecords.stream()
                            .sorted((r1, r2) -> r2.getAnsweredAt().compareTo(r1.getAnsweredAt()))
                            .limit(RECENT_ANSWERS_LIMIT)
                            .toList();
                    if (recentRecords.isEmpty()) return 0.0;
                    long correctCount = recentRecords.stream().filter(AnswerRecord::isCorrect).count();
                    return (double) correctCount / recentRecords.size();
                }));
    }


    public ClassAnalyticsResponseDto getAnalyticsForClass(Long classId, User teacher) {
        Class myClass = classRepository.findById(classId)
                .orElseThrow(() -> new EntityNotFoundException("班级未找到"));
        if (!myClass.getTeacher().getUserId().equals(teacher.getUserId())) {
            throw new AccessDeniedException("您无权查看此班级的学情报告");
        }

        String subject = myClass.getSubject();

        ClassAnalyticsResponseDto analytics = new ClassAnalyticsResponseDto();
        analytics.setTotalStudents(classMemberRepository.countByMyClass(myClass));

        List<StudentAssignment> gradedSubmissions = studentAssignmentRepository.findAllGradedByClassId(classId);
        Map<Assignment, Double> avgScores = gradedSubmissions.stream()
                .filter(sa -> sa.getAssignment().getMyClass().getSubject().equals(subject))
                .collect(Collectors.groupingBy(StudentAssignment::getAssignment,
                        Collectors.averagingDouble(sa -> sa.getScore() != null ? sa.getScore() : 0.0)));

        List<AssignmentStatDto> assignmentStats = avgScores.entrySet().stream()
                .map(entry -> new AssignmentStatDto(entry.getKey().getAssignmentId(), entry.getKey().getTitle(), entry.getValue()))
                .sorted(Comparator.comparing(AssignmentStatDto::getAssignmentId).reversed())
                .toList();
        analytics.setAssignmentStats(assignmentStats);

        List<AnswerRecord> incorrectRecords = answerRecordRepository.findAllIncorrectRecordsByClassId(classId);
        Map<Question, Long> errorCounts = incorrectRecords.stream()
                .map(AnswerRecord::getQuestion)
                .filter(question -> question.getKnowledgePoints().stream()
                        .anyMatch(kp -> subject.equals(kp.getSubject())))
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        List<CommonErrorQuestionDto> commonErrors = errorCounts.entrySet().stream()
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .limit(COMMON_ERRORS_LIMIT)
                .map(entry -> new CommonErrorQuestionDto(entry.getKey(), entry.getValue()))
                .toList();
        analytics.setCommonErrorQuestions(commonErrors);

        return analytics;
    }


    private record KpRecordPair(KnowledgePoint kp, AnswerRecord record) {
    }
}